﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Tweener : MonoBehaviour {
	
	//Tweener's all parameter
	private Dictionary<string,object> tweenArgs;
	
	//Primary parameter for Tweener
	public string tweenID;
	public string tweenType;
	public string tweenMethod;
	public string tweenStatus;
	
	//Object that EKTween will apply effect on
	public GameObject targetObject;
	
	//Target position and rotation using to MoveTo and RotateTo method
	private Vector3 targetPosition;
	private Vector3 targetRotation;
	
	//From A to B using to ValueTo method
	private object fromObj;
	private object toObj;
	
	//Time that taken to do any method
	public float spendTime;
	
	//Time that token to wait for excute any method
	public float delayTime;
	
	//Ease type
	private EKTween.EaseType easeType;
	
	//Callback type
	private EKTween.CallBackType callbackType;
	
	void Awake () {
		
		//Retrieve arguments from dictionary for Tweener
		RetrieveArgs();
	}
	
	// Use this for initialization
	IEnumerator Start () {
		
		//Wait for delay time and start tweener
		if(delayTime > 0) {
			yield return new WaitForSeconds(delayTime);
		}
		
		//Start the tween
		LetsTween();
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	
	//Retrieve arguments for this tween
	public void RetrieveArgs () {
		
		//Search for suitable arguments
		foreach(Dictionary<string,object> repo in EKTween.repoArgs) {
			if((GameObject)repo["target"] == gameObject) {
				tweenArgs = repo;
				EKTween.repoArgs.Remove(repo);
				break;
			}
		}
		
		//Basic parameter
		tweenID = (string)tweenArgs["id"];
		tweenType = (string)tweenArgs["type"];
		tweenMethod = (string)tweenArgs["method"];
		targetObject = (GameObject)tweenArgs["target"];
		
		if(tweenArgs.ContainsKey("easetype")) {
			easeType = (EKTween.EaseType)tweenArgs["easetype"];
		}
		else {
			easeType = EKTween.EaseType.Linear;
		}
		
		//Time that using to do this tween
		spendTime = (float)tweenArgs["time"];
		
		//Time that tweener wait for start
		if(tweenArgs.ContainsKey("delay")) {
			delayTime = (float)tweenArgs["delay"];
		}
		
		//Tween type
		switch(tweenType) {
		case "move":
			targetPosition = (Vector3)tweenArgs["position"];
			break;
		case "rotate":
			targetRotation = (Vector3)tweenArgs["rotation"];
			break;
		case "value":
			fromObj = tweenArgs["from"];
			toObj = tweenArgs["to"];
			break;
		}
	}
	
	//Start the tweener
	public void LetsTween () {
		
		CallBacks(); //Call for callbacks function
		StartCoroutine(AutoKill()); // Call for auto kill tween instance
		
		switch(tweenType) {
			
		case "move":
			switch(tweenMethod) {
			case "to":
				StartCoroutine(MoveTo());
				break;
			}
			break;
			
		case "rotate":
			switch(tweenMethod) {
			case "to":
				StartCoroutine(RotateTo());
				break;
			}
			break;
			
		case "value":
			switch(tweenMethod) {
			case "":
				StartCoroutine(ValueTo());
				break;
			}
			break;
		}
	}
	
	//***********************************************************************************
	// Tween type
	//***********************************************************************************
	
	private IEnumerator MoveTo () {
		
		switch(easeType) {
		case EKTween.EaseType.EaseOutQuard:
			yield return StartCoroutine(EaseOutQuardMove(transform.position, targetPosition, spendTime));
			break;
		
		case EKTween.EaseType.Linear:
			yield return StartCoroutine(LinearMove(transform.position, targetPosition, spendTime));
			break;
		}

	}
	
	private IEnumerator ValueTo () {
		
		string onUpdateFunction = (string)tweenArgs["onupdate"];
		
		if(fromObj.GetType() == typeof(Rect)) {
			
			Rect fromRect = (Rect)fromObj;
			Rect toRect = (Rect)toObj;
			Rect newRect = new Rect(fromRect);
			
			yield return StartCoroutine(ValueTo_Rect(fromRect, toRect, spendTime, onUpdateFunction));
		}
		
		
	}
	
	private void UpdateRect (string onUpdateFunction, Rect newRect) {
		targetObject.BroadcastMessage(onUpdateFunction, newRect, SendMessageOptions.DontRequireReceiver);
	}
	
	private IEnumerator ValueTo_Rect (Rect rfrom, Rect rto, float moveTime, string onUpdateFunction) {
		float t = 0.0f;
		float rate = 1.0f/moveTime;
		Rect newRect = new Rect(rfrom);
		while (t < 1.0f) {
			t = t + Time.deltaTime * rate;
			newRect.x = Mathf.Lerp(rfrom.x, rto.x, t);
			newRect.y = Mathf.Lerp(rfrom.y, rto.y, t);
			newRect.width = Mathf.Lerp(rfrom.width, rto.width, t);
			newRect.height = Mathf.Lerp(rfrom.height, rto.height, t);
			
			UpdateRect(onUpdateFunction, newRect);
			yield return null;
		}
	}
	
	//Easetype: Linear for Move
	private IEnumerator LinearMove (Vector3 startPos, Vector3 endPos, float moveTime) {
		float t = 0.0f;
		float rate = 1.0f/moveTime;
		while (t < 1.0f) {
			t = t + Time.deltaTime * rate;
			transform.position = Vector3.Lerp(startPos, endPos, t);
			yield return null;
		}
	}
	
	//Easetype: OutQuard for Move
	private IEnumerator EaseOutQuardMove (Vector3 startPos, Vector3 endPos, float moveTime) {
		float t = 0.0f;
		float rate = 1.0f/moveTime;
		while (t <= 1.0f) {		
			t = t + Time.deltaTime * rate;
			//transform.position = Vector3.MoveTowards(transform.position, targetPosition, SmootStep(1,t));
			//transform.position = Vector3.Lerp(transform.position, end.position, Time.time);
			transform.position = Vector3.Lerp(transform.position, endPos, SmootStep(0,t));
			yield return 0;
		}
	}
	
	private IEnumerator RotateTo () {
		
		//float t = 0.0f;
		
		Quaternion targetQuaternion = Quaternion.Euler(targetRotation);
		Debug.Log(transform.rotation);
		
		while (transform.rotation != targetQuaternion) {		
			//t += Time.deltaTime/spendTime;
			transform.rotation = Quaternion.Lerp(transform.rotation, targetQuaternion, Time.deltaTime*10);
			yield return 0;
		}
	}
	
	//Call for callback functions
	private void CallBacks () {
		if(tweenArgs.ContainsKey("onstart")) {
			OnStart();
		}
		
		if(tweenArgs.ContainsKey("oncomplete")) {
			StartCoroutine(OnComplete());
		}
		
		if(tweenArgs.ContainsKey("onupdate")) {
			OnUpdate();
		}
	}
	
	//Call for auto kill after end of this tween
	private IEnumerator AutoKill () {
		yield return new WaitForSeconds(spendTime);
		EKTween.Kill(this);
	}
	
	//Callback - On Start
	private void OnStart () {
		
	}
	
	//Callback - On Complete
	private IEnumerator OnComplete () {
		
		//Wait for complete of tween
		yield return new WaitForSeconds(spendTime);
		
		//Get callback function
		string message = tweenArgs["oncomplete"].ToString();
		
		GameObject completeTarget = new GameObject();
		if(tweenArgs.ContainsKey("oncompletetarget")) {
			completeTarget = (GameObject)tweenArgs["oncompletetarget"];
		}
		else {
			completeTarget = gameObject;
		}
		
		//Parameter for broadcast message
		if(tweenArgs.ContainsKey("oncompleteparams")) {
			object comParams = tweenArgs["oncompleteparams"];
			completeTarget.BroadcastMessage(message, comParams, SendMessageOptions.DontRequireReceiver);
		}
		else {
			completeTarget.BroadcastMessage(message, SendMessageOptions.DontRequireReceiver);
		}
	}
	
	//Callback - On Update
	private void OnUpdate () {
		
	}
	
	
	//***********************************************************************************
	// Utilities functions
	//***********************************************************************************
	float SmootStep (int intensity, float t) {
		
		float smoothStep = Mathf.SmoothStep(0.0f, 1.0f, t);
		
		for(int i=0; i<intensity-1; i++) {
			smoothStep = Mathf.SmoothStep(0.0f, 1.0f, smoothStep);
		}
		
		return smoothStep;
	}
}
